package com.jake.xpage.page;

import android.util.SparseArray;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class XPageManagerImpl implements XPageManger {
    @NonNull
    private final LinkedList<XPage> mStack = new LinkedList<>();
    @NonNull
    private final Set<OnPagesChangedListener> mPagesChangedListeners = new HashSet<>();
    @NonNull
    private final XPageContainer mContainer;
    @NonNull
    private final XPage.Callback mPageCallback;

    private IXPageConfigHandler mPageConfigBarHandler;
    @NonNull
    private final SparseArray<List<XPage>> mTransparentPages = new SparseArray<>();

    public XPageManagerImpl(@NonNull XPageContainer container, @NonNull XPage.Callback callback) {
        mContainer = container;
        mPageCallback = callback;
    }

    public void setPageConfigHandler(IXPageConfigHandler handler) {
        mPageConfigBarHandler = handler;

    }


    public void push(XPage page, IPageAnimator animator) {
        if (page == null) {
            return;
        }
        page.setCallback(mPageCallback);
        final XPage backPage = mStack.peek();
        notifyPagesChanged(OnPagesChangedListener.EVENT_BEFORE_PUSH, backPage, page);
        mStack.push(page);
        final XPage frontPage = page;
        //回调前台page生命周期
        frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
        mContainer.addView(page);
        if (animator != null) {
            animator.start(IPageAnimator.Type.PUSH, frontPage, backPage, new IPageAnimator.OnAnimateListener() {
                @Override
                public void onStart() {
                    frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_START);
                }

                @Override
                public void onEnd() {
                    if (backPage != null) {
                        backPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
                    }
                    configPage(frontPage);
                    frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
                    if (backPage != null) {
                        backPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_STOP);
                        if (frontPage.pageInfo.transparent) {
                            int key = frontPage.hashCode();
                            addToTransparentPages(key, backPage);
                        } else {
                            hideBackPage(backPage);
                        }
                    }
                    notifyPagesChanged(OnPagesChangedListener.EVENT_AFTER_PUSH, backPage, page);
                }
            });
        } else {
            frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_START);
            if (backPage != null) {
                backPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
            }
            frontPage.post(new Runnable() {//延后一个消息队列处理,解决第一个page没有陪着的问题
                @Override
                public void run() {
                    configPage(frontPage);
                }
            });

            frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
            if (backPage != null) {
                backPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_STOP);
                if (frontPage.pageInfo.transparent) {
                    int key = frontPage.hashCode();
                    addToTransparentPages(key, backPage);
                } else {
                    hideBackPage(backPage);
                }
            }
            notifyPagesChanged(OnPagesChangedListener.EVENT_AFTER_PUSH, backPage, page);
        }

    }

    private void hideBackPage(@NonNull XPage backPage) {
        int key = backPage.hashCode();
        List<XPage> pages = mTransparentPages.get(key);
        if (pages != null) {
            for (XPage page : pages) {
                if (page != null) {
                    page.setVisibility(View.GONE);
                }
            }
        }
        backPage.setVisibility(View.GONE);
    }

    private void addToTransparentPages(int key, @NonNull XPage page) {
        List<XPage> pages = mTransparentPages.get(key);
        if (pages == null) {
            pages = new ArrayList<>();
        }
        pages.add(page);
        mTransparentPages.put(key, pages);
    }

    private boolean canPop() {
        return mStack.size() > 1;
    }

    public void pop(IPageAnimator animator) {
        if (!canPop()) {
            return;
        }
        final XPage frontPage = mStack.pop();
        final XPage backPage = mStack.peek();
        popInner(frontPage, backPage, animator);
    }

    private void popInner(final XPage frontPage, final XPage backPage, IPageAnimator animator) {
        notifyPagesChanged(OnPagesChangedListener.EVENT_BEFORE_POP, frontPage, backPage);
        if (backPage != null) {
            backPage.setVisibility(View.VISIBLE);
            int key = backPage.hashCode();
            List<XPage> pages = mTransparentPages.get(key);
            if (pages != null) {
                for (XPage page : pages) {
                    if (page != null) {
                        page.setVisibility(View.VISIBLE);
                    }
                }
            }
        }
        if (animator != null) {
            animator.start(IPageAnimator.Type.POP, frontPage, backPage, new IPageAnimator.OnAnimateListener() {
                @Override
                public void onStart() {
                    if (backPage != null) {
                        backPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_START);
                    }
                }

                @Override
                public void onEnd() {
                    frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
                    if (backPage != null) {
                        configPage(backPage);
                        backPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
                    }
                    frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_STOP);
                    mContainer.removeView(frontPage);
                    mTransparentPages.remove(frontPage.hashCode());
                    frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
                    notifyPagesChanged(OnPagesChangedListener.EVENT_AFTER_POP, frontPage, backPage);
                }
            });

        } else {
            frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
            if (backPage != null) {
                configPage(backPage);
                backPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
            }
            frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_STOP);
            mContainer.removeView(frontPage);
            mTransparentPages.remove(frontPage.hashCode());
            frontPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
            notifyPagesChanged(OnPagesChangedListener.EVENT_AFTER_POP, frontPage, backPage);
        }
    }

    private void configPage(@NonNull XPage page) {
        if (mPageConfigBarHandler != null) {
            mPageConfigBarHandler.configPage(page, page.getPageInfo());
        }
    }

    public void popTo(XPage page, IPageAnimator animator) {
        if (page == null || !canPop() || !mStack.contains(page)) {
            return;
        }
        int count = mStack.size();
        if (count < 2) {
            return;
        }
        final XPage frontPage = mStack.peek();
        int target = mStack.indexOf(page);
        for (int i = count - 2; i > target; i--) {
            removeInner(i, false);
        }
        popInner(frontPage, page, animator);
    }


    public XPage peek() {
        return mStack.peek();
    }

    public XPage peekSecond() {
        if (mStack.size() > 1) {
            return mStack.get(mStack.size() - 2);
        }
        return null;
    }

    public void remove(int pageIndex) {
        removeInner(pageIndex, true);
    }

    private void removeInner(int pageIndex, boolean notifyChanged) {
        if (pageIndex < 0 || pageIndex >= mStack.size()) {
            return;
        }
        XPage current = mStack.peek();
        XPage targetPage = mStack.get(pageIndex);
        if (notifyChanged) {
            notifyPagesChanged(OnPagesChangedListener.EVENT_BEFORE_REMOVE, current, targetPage);
        }
        mStack.remove(targetPage);
        mContainer.removeView(targetPage);
        targetPage.getLifecycle().handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
        if (notifyChanged) {
            notifyPagesChanged(OnPagesChangedListener.EVENT_AFTER_REMOVE, current, targetPage);
        }
    }

    public void remove(XPage page) {
        if (page == null) {
            return;
        }
        int target = mStack.indexOf(page);
        if (target >= 0) {
            remove(target);
        }
    }

    @NonNull
    @Override
    public LinkedList<XPage> getAllPages() {
        LinkedList<XPage> list = new LinkedList<>(mStack);
        return list;
    }

    private void notifyPagesChanged(int event, XPage current, XPage target) {
        Iterator<OnPagesChangedListener> it = mPagesChangedListeners.iterator();
        while (it.hasNext()) {
            OnPagesChangedListener listener = it.next();
            if (listener != null) {
                listener.onChanged(event, current, target);
            }
        }
    }

    @Override
    public void addOnPagesChangedListener(OnPagesChangedListener listener) {
        if (listener != null) {
            mPagesChangedListeners.add(listener);
        }
    }

    @Override
    public void removeOnPagesChangedListener(OnPagesChangedListener listener) {
        if (listener != null) {
            mPagesChangedListeners.remove(listener);
        }
    }
}
